﻿using System;
using System.Windows;
using System.Windows.Controls;
using StackCalc.Parsing;

namespace StackCalc
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow
	{
		/// <summary>
		/// Used to parse the input.
		/// </summary>
		private readonly InputParser _inputParser = new InputParser();

		private readonly StackEngine _stackEngine = new StackEngine();

		private string _applyBuffer = string.Empty;

		private Action<string> _readCallback;

		public MainWindow()
		{
			InitializeComponent();

			_stackEngine.ApplyEvent += HandleApply;
			_stackEngine.PrintEvent += HandlePrint;
			_stackEngine.ReadEvent += HandleRead;
		}

		private void HandleRunAllClick(object sender, RoutedEventArgs e)
		{
			while (btnStep.IsEnabled)
			{
				string input = txtInput.Text;

				// parse instruction
				_stackEngine.Process(_inputParser.Parse(input));

				// update stack
				txtStack.Text = _stackEngine.PrintStack();

				// trunkate input, also add the result of an eventual apply action
				txtInput.Text = _applyBuffer + _inputParser.Trunkate(input);
				_applyBuffer = string.Empty;
			}
		}

		private void HandleStepClick(object sender, RoutedEventArgs e)
		{
			string input = txtInput.Text;

			// parse instruction
			_stackEngine.Process(_inputParser.Parse(input));

			// update stack
			txtStack.Text = _stackEngine.PrintStack();

			// trunkate input, also add the result of an eventual apply action
			txtInput.Text = _applyBuffer + _inputParser.Trunkate(input);
			_applyBuffer = string.Empty;
		}

		private void HandleResetClick(object sender, RoutedEventArgs e)
		{
			txtInput.Clear();
			txtStack.Text = string.Empty;
			txtOutput.Text = string.Empty;
			txtExpression.Clear();
			txtExpression.IsEnabled = false;
			txtInput.IsEnabled = true;

			_stackEngine.Reset();
		}

		private void HandleTextInputChanged(object sender, TextChangedEventArgs e)
		{
			UpdateInputTextState();
		}

		private void UpdateInputTextState()
		{
			string input = txtInput.Text;

			// update syntax error label on the fly
			if (_inputParser.HasSyntaxError(input) && txtInput.IsEnabled)
			{
				DisplayError(_inputParser.GetErrorDescription(input));
			}
			else if (string.IsNullOrEmpty(input.Trim()))
			{
				DisplayError("Input is empty!");
			}
			else if (_stackEngine.HasSemanticError(_inputParser.Parse(input)) && txtInput.IsEnabled)
			{
				DisplayError(_stackEngine.ErrorText);
			}
			else if (txtInput.IsEnabled)
			{
				HideError();
			}
		}

		private void DisplayError(string errorText)
		{
			// display the given error text and disable buttons
			lblError.Content = errorText;
			lblError.Visibility = Visibility.Visible;
			btnStep.IsEnabled = false;
			btnRunAll.IsEnabled = false;
		}

		private void HideError()
		{
			lblError.Visibility = Visibility.Hidden;
			btnStep.IsEnabled = true;
			btnRunAll.IsEnabled = true;
		}

		private void HandleButtonExpressionClick(object sender, RoutedEventArgs e)
		{
			// exit "read mode"
			txtInput.IsEnabled = true;

			txtExpression.IsEnabled = false;

			_readCallback(txtExpression.Text);
			UpdateInputTextState();
			txtExpression.Clear();
			txtStack.Text = _stackEngine.PrintStack();
		}

		private void HandleExpressionTextChanged(object sender, TextChangedEventArgs e)
		{
			if (!string.IsNullOrEmpty(txtExpression.Text))
			{
				btnEnterExpression.IsEnabled = true;
			}
			else
			{
				btnEnterExpression.IsEnabled = false;
			}
		}

		private void HandleApply(object sender, EventArgs<string> e)
		{
			// only store in buffer, add after next truncation action
			_applyBuffer = " " + e.Value + " ";
		}


		private void HandlePrint(object sender, EventArgs<string> e)
		{
			// "undo" the auto escaping done by textboxes
			txtOutput.Text += e.Value.Replace("\\n", "\n").Replace("\\t", "\t").Replace("\\", "");
		}

		private void HandleRead(object sender, EventArgs<Action<string>> e)
		{
			// enable "read mode"
			txtInput.IsEnabled = false;
			btnStep.IsEnabled = false;
			btnRunAll.IsEnabled = false;

			txtExpression.IsEnabled = true;

			_readCallback = e.Value;
		}
	}
}
